package top.atcra.rpc.annotation.scanner;

import cn.hutool.core.lang.Assert;
import lombok.extern.slf4j.Slf4j;
import top.atcra.rpc.annotation.RPCService;
import top.atcra.rpc.annotation.RPCServiceImpl;
import top.atcra.rpc.service.ServiceConfig;

import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

@Slf4j
public class RPCAnnotationScanner {
    /**
     * 扫描包，进行批量注册
     *
     * @param packageName 包名
     * @return <服务,服务实现类>列表
     */
    public List<ServiceConfig> serviceImplementScan(String packageName) {
        List<ServiceConfig> result = new ArrayList<>();
        // 1、需要通过packageName获取其下的所有的类的权限定名称
        List<String> classNames = getAllClassNames(packageName);
        // 2、通过反射获取他的接口，构建具体实现
        var classes = classNames.stream()
                .map(className -> {
                    try {
                        return Class.forName(className);
                    } catch (ClassNotFoundException e) {
                        throw new RuntimeException(e);
                    }
                }).filter(clazz -> clazz.getAnnotation(RPCServiceImpl.class) != null)
                .toList();

        for (Class<?> clazz : classes) {
            Class<?>[] interfaces = clazz.getInterfaces();
            Assert.notEmpty(interfaces, "服务实现类{}必须实现接口", clazz);
            try {
                clazz.getConstructor();
            } catch (NoSuchMethodException e) {
                throw new RuntimeException(String.format("服务实现类%s必须提供无参构造器", clazz.getName()));
            }

            // 获取分组信息
            RPCServiceImpl RPCServiceImpl = clazz.getAnnotation(RPCServiceImpl.class);
            String group = RPCServiceImpl.value();

            for (Class<?> anInterface : interfaces) {
                // 只添加包含@RPCService注解的接口
                var serviceAnnotation = anInterface.getAnnotation(RPCService.class);
                if (serviceAnnotation == null) {
                    continue;
                }
                String serviceName = serviceAnnotation.value() == null || serviceAnnotation.value().isEmpty() ?
                        anInterface.getName() : serviceAnnotation.value();
                try {
                    ServiceConfig serviceDef =
                            new ServiceConfig(serviceName).setGroup(group)
//                            .setInterfaceClass(anInterface)
                                    .setRef(clazz.getConstructor().newInstance());
                    result.add(serviceDef);
                } catch (InstantiationException | IllegalAccessException | InvocationTargetException |
                         NoSuchMethodException e) {
                    throw new RuntimeException(e);
                }
                if (log.isDebugEnabled()) {
                    log.debug("扫描{}，发现服务{}的{}分组实现【{}】", packageName, serviceName, group, clazz.getName());
                }
            }
        }
        return result;
    }

    private List<String> getAllClassNames(String packageName) {
        // 通过packageName获得文件系统绝对路径
        String basePath = packageName.replaceAll("\\.", "/");
        URL url = ClassLoader.getSystemClassLoader().getResource(basePath);
        if (url == null) {
            throw new RuntimeException("包扫描时，发现路径不存在.");
        }
        return recursionFile(url.getPath(), new ArrayList<>(), basePath);
    }

    private List<String> recursionFile(String absolutePath, List<String> classNames, String basePath) {
        // 获取文件
        File file = new File(absolutePath);
        // 判断文件是否是文件夹
        if (file.isDirectory()) {
            // 找到文件夹的所有的文件
            File[] children = file.listFiles(pathname -> pathname.isDirectory() || pathname.getPath().contains(".class"));
            if (children == null) {
                return classNames;
            }
            for (File child : children) {
                if (child.isDirectory()) {
                    recursionFile(child.getAbsolutePath(), classNames, basePath);
                } else {
                    // 文件 --> 类的权限定名称
                    String className = getClassNameByAbsolutePath(child.getAbsolutePath(), basePath);
                    classNames.add(className);
                }
            }

        } else {
            // 文件 --> 类的权限定名称
            String className = getClassNameByAbsolutePath(absolutePath, basePath);
            classNames.add(className);
        }
        return classNames;
    }

    private String getClassNameByAbsolutePath(String absolutePath, String basePath) {
        String fileName = absolutePath
                .substring(absolutePath.indexOf(basePath.replaceAll("/", "\\\\")))
                .replaceAll("\\\\", ".");

        fileName = fileName.substring(0, fileName.indexOf(".class"));
        return fileName;
    }
}
